﻿using System;

namespace sinetd
{
    /// <summary>
    /// Manages all protocols and services on server side.
    /// </summary>
    public class Server : ServerController
    {
        #region MessageLog

        /// <summary>
        /// Represents the method that will handle the sinetd.Server.MessageLog event of a sinetd.Server class.
        /// </summary>
        /// <param name="msgType">The type of message.</param>
        /// <param name="msgText">The text of message.</param>
        public delegate void MessageLogEventHandler(string msgType, string msgText);

        /// <summary>
        /// Occurs when server sends messages to log.
        /// </summary>
        public event MessageLogEventHandler MessageLog;

        /// <summary>
        /// Raises the sinetd.Server.MessageLog event.
        /// </summary>
        /// <param name="msgType"></param>
        /// <param name="msgText"></param>
        private void OnMessageLog(string msgType, string msgText)
        {
            if (MessageLog != null)
                MessageLog(msgType, msgText);
        }

        #endregion

        /// <summary>
        /// Initializes a new instance of sinetd.Server class.
        /// Also load default config from "Server.config.xml".
        /// </summary>
        public Server()
        {
            cfg = new Configuration();
            cfg.Load("Server.config.xml");
        }

        /// <summary>
        /// Initializes a new instance of sinetd.Server class.
        /// </summary>
        /// <param name="pathToConfiguration">Full path to file contains configuration.</param>
        public Server(string pathToConfiguration)
        {
            cfg = new Configuration();
            cfg.Load(pathToConfiguration);
        }

        /// <summary>
        /// Stores configuration of server.
        /// </summary>
        private Configuration cfg;

        /// <summary>
        /// Occurs on a new connection from any protocol and redirects it to proper service. 
        /// </summary>
        /// <param name="prot">The instance of protocol from new connection.</param>
        private void AcceptClient(Protocols.IProtocol prot)
        {
            try
            {
                Services.IService serv = cfg.GetService(prot.Id);
                OnMessageLog("Server.AcceptClient", "Connected via " + prot.Id + " to service " + serv.Id);
                serv.HandleNewConnection(prot);
            }
            catch (Exception ex)
            {
                OnMessageLog("Server.AcceptClient", ex.Message);
            }
        }

        #region Implementation of abstract method from sinetd.ServerController

        /// <summary>
        /// Loads and starts all services and bind protocols defined in configuration.
        /// </summary>
        protected override void Begin()
        {
            Protocols.AcceptClientHandler handler = new Protocols.AcceptClientHandler(AcceptClient);

            foreach (Services.IService serv in cfg.Services.Values)
            {
                try
                {
                    OnMessageLog("Server.Begin", "[ " + serv.Name + " ] ...");
                    foreach (Protocols.IProtocol prot in cfg.Serv2Prot(serv))
                    {
                        OnMessageLog("Server.Begin", "\t" + prot.Name + " ...");
                        prot.Bind(handler);
                    }
                }
                catch (Exception ex)
                {
                    OnMessageLog("Server.Begin", ex.Message);
                }
            }
        }

        /// <summary>
        /// Empty method - occurs on waiting for new connection, once per Delay in milliseconds. 
        /// </summary>
        protected override void Run()
        {
            try
            {
            }
            catch (Exception ex)
            {
                OnMessageLog("Server.Run", ex.Message);
            }
        }

        /// <summary>
        /// Call close on all instances of sinetd.Protocols.IProtocols and shutdown on all instances of sinted.Services.IService.
        /// </summary>
        protected override void End()
        {
            foreach (Services.IService serv in cfg.Services.Values)
            {
                foreach (Protocols.IProtocol prot in cfg.Serv2Prot(serv))
                {
                    try
                    {
                        OnMessageLog("Server.End", prot.Name + " closing");
                        prot.Close();
                    }
                    catch (Exception ex)
                    {
                        OnMessageLog("Server.End", ex.Message);
                    }
                }

                try
                {
                    OnMessageLog("Server.End", serv.Id + " shutting down");
                    serv.Shutdown();
                }
                catch (Exception ex)
                {
                    OnMessageLog("Server.End", ex.Message);
                }
            }
        }

        #endregion
    }
}
